/**
 * 项目名：	Rumba Plan
 * 
 * 版权所有(C)，上海海鼎信息工程股份有限公司，2006，所有权利保留。
 * 
 * 文件名：	BaseHField.java
 * 模块说明：	字段控件基类 | 服务端界面控件
 * 修改历史：
 * 2006-5-25 - lxm - 创建。
 */
package com.hd123.rumba.jsp;

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;

import org.apache.ecs.ConcreteElement;

import com.hd123.rumba.request.Dictionary;
import com.hd123.rumba.request.JspLib;
import com.hd123.rumba.util.AccessMode;

/**
 * 字段控件基类 | 服务端界面控件
 * 
 * @author lxm
 * 
 */
public abstract class BaseHField {

  /**
   * 构造函数。
   * 
   * @param dd
   *          数据字典
   * @param name
   *          字段名，用于标识
   * @param caption
   *          标题
   * @param value
   *          字段取值
   * @param params
   *          其它参数数组。其允许的参数包括两种：<br> - 一般参数。可用的参数说明参见相应的属性说明。<br> -
   *          界面元素参数。以元素的标识名（例如"tdLabel"）开头，后一个为元素属性名，再后面一个为元素属性取值。
   */
  public BaseHField(Dictionary dd, String name, String caption, Object value, Object[] params) {
    initialize(dd, name, caption, value, params);
  }

  public BaseHField(Dictionary dd, String name, String caption, Object value) {
    initialize(dd, name, caption, value, new Object[] {});
  }

  private Dictionary dd;
  private String name;
  private String caption;
  private Object value = null;
  private String permissionId = null;
  private Boolean isRequired = Boolean.FALSE;
  private Integer businessAccessMode = VALUE_BAM_NONE;
  protected Hashtable<String, KeyValueList> componentAttrs = new Hashtable();

  /* 访问模式 */
  protected int accessMode;
  /* 提示模式 */
  protected int promptMode;
  /* 是否已经构造 */
  protected boolean isBuilt = false;

  /** 属性businessAccessMode的取值。表示无约束。 */
  public static final Integer VALUE_BAM_NONE = new Integer(0);
  /** 属性businessAccessMode的取值。表示最高访问权限为只读。 */
  public static final Integer VALUE_BAM_READONLY = new Integer(1);
  /** 属性businessAccessMode的取值。表示总是处于可读写状态，将忽略权限和formState。 */
  public static final Integer VALUE_BAM_ALWAYSWRITABLE = new Integer(2);

  /** 提示模式取值：正常 */
  protected static final int VALUE_PROMPTMODE_NORMAL = 0;
  /** 提示模式取值：显示错误 */
  protected static final int VALUE_PROMPTMODE_ERROR = 1;

  /**
   * 属性：业务访问模式。<br>
   * 对应的参数名"businessAccessMode"。下一个参数为取值，数据类型为Integer，可用的取值参见"VALUE_BAM_"开头的常量的说明。
   */
  public Integer getBusinessAccessMode() {
    return businessAccessMode;
  }

  public void setBusinessAccessMode(Integer businessAccessMode) {
    this.businessAccessMode = businessAccessMode;
  }

  /** 属性：标题 */
  public String getCaption() {
    return caption;
  }

  public void setCaption(String caption) {
    this.caption = caption;
  }

  /** 属性：数据字典 */
  public Dictionary getDd() {
    return dd;
  }

  public void setDd(Dictionary dd) {
    this.dd = dd;
  }

  /**
   * 属性：是否必须字段。<br>
   * 对应参数名"isRequired"。下一个参数为取值，类型为Boolean。
   */
  public Boolean getIsRequired() {
    return isRequired;
  }

  public void setIsRequired(Boolean isRequired) {
    this.isRequired = isRequired;
  }

  /** 属性：名称，用于标识 */
  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  /**
   * 属性：对应的权限ID。允许null，表示不受权限控制。<br>
   * 对应参数名"permissionId"，下一个参数为取值，类型为String。
   */
  public String getPermissionId() {
    return permissionId;
  }

  public void setPermissionId(String permissionId) {
    this.permissionId = permissionId;
  }

  /** 属性：取值，允许null。 */
  public Object getValue() {
    return value;
  }

  public void setValue(Object value) {
    this.value = value;
  }

  /**
   * 构造控件。用于提前构造控件后，使得调用者可以在toHTML前进行客户化。
   * 
   */
  public void build() {
    String formState = (String) dd.getFormValue(JspLib.ID_FORMSTATE);
    accessMode = getAccessMode(formState);
    promptMode = getPromptMode();
    buildHTML(accessMode, promptMode);
    isBuilt = true;
  }

  /**
   * 输出HTML字符串。
   * 
   * @return 返回符合HTML语法的字符串，可以将其直接嵌入页面使用。
   */
  public String toHTML() {
    if (!isBuilt)
      build();
    return "";
  }

  /**
   * 设置界面元素的属性参数。
   * 
   * @param e
   *          界面元素对象
   * @param elementName
   *          界面元素的标识名。
   */
  protected void assignElementAttrs(ConcreteElement e, String elementName) {
    KeyValueList eAttrs = componentAttrs.get(elementName);
    if (eAttrs == null)
      return;
    for (int i = 0; i < eAttrs.size(); i++) {
      String key = (String) eAttrs.getKey(i);
      e.addAttribute(key, eAttrs.get(key));
    }
  }

  /**
   * 在界面元素间复制属性参数。
   * 
   * @param source
   *          指定赋值来源界面元素。
   * @param target
   *          指定赋值目标界面元素。
   * @param excludedAttrs
   *          指定被排除复制的属性名数组。null表示不排除。
   */
  protected void assignElementAttrs(ConcreteElement source, ConcreteElement target,
      String[] excludedAttrs) {
    Enumeration attrs = source.attributes();
    while (attrs.hasMoreElements()) {
      String attr = (String) attrs.nextElement();

      // 检查是否是需要被排除的属性。
      if (excludedAttrs != null) {
        boolean excluded = false;
        for (String attr2 : excludedAttrs)
          if (attr.equalsIgnoreCase(attr2)) {
            excluded = true;
            break;
          }
        if (excluded)
          continue;
      }

      target.addAttribute(attr, source.getAttribute(attr));
    }
  }

  /**
   * 读取参数。
   * <p>
   * 开放给派生类以重写，用于新增加参数的解析。
   * 
   * @param paramName
   *          参数名
   * @param paramValue
   *          参数值
   * @return true/false，是否被成功解释。
   */
  protected boolean readParam(String paramName, Object paramValue) {
    return false;
  }

  /**
   * 取得访问模式
   * 
   * @param formState
   *          页面的状态。取值：JspLib.VALUE_FORMSTATE_VIEW | JspLib.VALUE_FORMSTATE_EDIT |
   *          JspLib.VALUE_FORMSTATE_CREATE
   * @return 返回访问模式。取值：AccessMode.INVISIBLE | AccessMode.READONLY |
   *         AccessMode.READWRITE
   */
  protected int getAccessMode(String formState) {
    if (formState == null)
      formState = (String) dd.getFormValue(JspLib.ID_FORMSTATE);
    if (formState == null)
      formState = JspLib.VALUE_FORMSTATE_VIEW;

    int mode = AccessMode.READWRITE;
    if (VALUE_BAM_ALWAYSWRITABLE.equals(businessAccessMode))
      mode = AccessMode.READWRITE;
    else {
      if (permissionId == null) {
        mode = AccessMode.READWRITE;
      } else {
        mode = AccessMode.INVISIBLE;
        AccessMode am = (AccessMode) dd.get("am");
        if (am != null) {
          mode = am.getAccessMode(permissionId);
          if (mode == -1)
            mode = AccessMode.INVISIBLE;
        }
      }
      if (JspLib.VALUE_FORMSTATE_EDIT.equals(formState)
          || JspLib.VALUE_FORMSTATE_CREATE.equals(formState))
        mode = Math.min(mode, AccessMode.READWRITE);
      else
        mode = Math.min(mode, AccessMode.READONLY);
      if (VALUE_BAM_READONLY.equals(businessAccessMode))
        mode = Math.min(mode, AccessMode.READONLY);
    }
    return mode;
  }

  /**
   * 取得提示模式
   * 
   * @return 返回提示模式。取值参见"VALUE_PROMPTMODE_"开头的常量的说明。
   */
  protected int getPromptMode() {
    if (dd.getInvalidFormValues().containsKey(name + JspLib.POSTFIX_FIELD_MISSING)
        || dd.getInvalidFormValues().containsKey(name + JspLib.POSTFIX_FIELD_INVALID)
        || dd.getInvalidFormValues().containsKey(name + JspLib.POSTFIX_FIELD_INVALIDPERM)
        || dd.getInvalidFormValues().containsKey(name + JspLib.POSTFIX_FIELD_ERROR))
      return VALUE_PROMPTMODE_ERROR;
    else
      return VALUE_PROMPTMODE_NORMAL;
  }

  /**
   * 构造页面。
   * 
   * @param accessMode
   *          访问模式
   * @param promptMode
   *          提示模式
   */
  protected abstract void buildHTML(int accessMode, int promptMode);

  private void initialize(Dictionary dd, String name, String caption, Object value, Object[] params) {
    this.dd = dd;
    this.name = name;
    this.caption = caption;
    this.value = value;

    for (int i = 0; i < params.length;) {
      String paramName = (String) params[i++];
      Object paramValue = params[i++];
      if ("permissionId".equals(paramName))
        this.permissionId = (String) paramValue;
      else if ("isRequired".equals(paramName))
        this.isRequired = (Boolean) paramValue;
      else if ("businessAccessMode".equals(paramName))
        this.businessAccessMode = (Integer) paramValue;
      else {
        if (readParam(paramName, paramValue))
          continue;
        String elementName = paramName;
        KeyValueList attrs = componentAttrs.get(elementName);
        if (attrs == null) {
          attrs = new KeyValueList();
          componentAttrs.put(elementName, attrs);
        }
        String attrName = (String) paramValue;
        Object attrValue = params[i++];
        attrs.put(attrName, attrValue);
      }
    }
  }

  public class KeyValueList extends ArrayList<Vector> {

    public void put(Object key, Object value) {
      Vector v = new Vector();
      v.add(key);
      v.add(value);
      this.add(v);
    }

    public Object get(Object key) {
      Object result = null;
      for (int i = 0; i < this.size(); i++) {
        if (this.get(i).get(0).equals(key)) {
          result = this.get(i).get(1);
          break;
        }
      }
      return result;
    }

    public Object getKey(int index) {
      return this.get(index).get(0);
    }

  }
}
